package com.groupYLP.CouponNexus.engine.dao.sharding;

import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;

import java.util.Collection;
import java.util.List;

/**
 * 实现基于模运算的表格散列分片算法类
 * 该类实现了StandardShardingAlgorithm接口，用于定义分片策略
 */
public class TableHashModShardingAlgorithm implements StandardShardingAlgorithm<Long> {

    /**
     * 执行精确分片
     * 根据给定的分片值和可用的目标名称集合，选择一个目标名称
     *
     * @param availableTargetNames 可用的目标名称集合，代表可以分片到的数据库或表
     * @param shardingValue 分片值，包含用于分片算法的精确值
     * @return 选择的目标名称
     * @throws IllegalArgumentException 如果没有找到合适的目标时抛出异常
     */
    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
        // 获取分片值
        long id = shardingValue.getValue();
        // 计算分片总数
        int shardingCount = availableTargetNames.size();
        // 计算散列分片值的模
        int mod = (int) hashShardingValue(id) % shardingCount;
        int index = 0;
        // 遍历目标名称集合，找到对应模值的目标名称
        for (String targetName : availableTargetNames) {
            if (index == mod) {
                return targetName;
            }
            index++;
        }
        // 如果没有找到合适的目标，抛出异常
        throw new IllegalArgumentException("No target found for value: " + id);
    }

    /**
     * 执行范围分片
     * 根据给定的范围分片值和可用的目标名称集合，选择目标名称的集合
     * 目前该方法默认返回空集合，因为尚未有范围分片的场景
     *
     * @param availableTargetNames 可用的目标名称集合
     * @param shardingValue 范围分片值
     * @return 选择的目标名称集合，目前默认返回空集合
     */
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) {
        // 暂无范围分片场景，默认返回空
        return List.of();
    }

    /**
     * 计算分片值的散列值
     * 使用绝对值确保散列值为正数，以避免负数的模运算问题
     *
     * @param shardingValue 分片值
     * @return 分片值的散列值
     */
    private long hashShardingValue(final Comparable<?> shardingValue) {
        return Math.abs((long) shardingValue.hashCode());
    }

}

